/*
 * File:    Convert.java
 * Created: 04-Feb-2009
 * Version: $Id$
 *
 * COPYRIGHT (C) 2009, Bitgate Mobile, LLC.  All Rights Reserved.
 *
 * software@bitgatemobile.com
 */

package com.webplasm.render.convert;

import static com.bitgate.util.debug.Debug.debug;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Set;
import java.util.Vector;

/**
 * This class is part of the conversion system used in Webplasm to convert normal non-marked up SSP pages into
 * well-formed XML pages.  It automatically looks for Webplasm elements that are recognized, and surrounds the
 * other code in &lt;![CDATA[ ]]&gt; tags, making the HTML tags rendered without being overridden by the XML
 * processor.
 * 
 * @author kenji
 * @since Webplasm 1.0
 */
public class Convert {

	private static final Object PLACEHOLDER_OBJECT = new Object();
	
	/**
	 * Prepares a loadable file for conversion, wrapping the unknown tags in CDATA tags so that the XML
	 * parser ignores those objects.
	 * 
	 * @param file The filename to load.
	 * @return <code>String</code> containing the converted output.
	 */
	public static String convertOutput(String file) {
		Set<String> keys = ConvertTagCache.getDefault().getTagNames();
		Set<String> subKeys = ConvertTagCache.getDefault().getSubtagNames();
		HashMap<String, Object> tagCache = new HashMap<String, Object>();
		HashMap<String, Object> subtagCache = new HashMap<String, Object>();
		
		for(String key : keys) {
			tagCache.put(key, PLACEHOLDER_OBJECT);
		}
		
		if (subKeys != null) {
			for(String key : subKeys) {
				subtagCache.put(key, PLACEHOLDER_OBJECT);
			}
		}
		
		StringBuffer fileBuffer = new StringBuffer();
		StringBuffer output = new StringBuffer();
		BufferedReader is = null;
		int knownTags = 0, unknownTags = 0;
		
		try {
			is = new BufferedReader(new FileReader(file));
		} catch(FileNotFoundException ex) {
			debug("File '" + file + "' cannot be converted: " + ex.getMessage());
			return null;
		}
		
		int linesRead = 0;
		
		while(true) {
			String line = null;
			
			try {
				line = is.readLine();
				if (line == null) {
					break;
				}
			} catch(Exception ex) {
				break;
			}
			
			fileBuffer.append(line + "\n");
			
			linesRead++;
			
			if (line.indexOf("<![CDATA[") != -1 || line.indexOf("]]>") != -1) {
				debug("Failed at line " + linesRead + ": <![CDATA[ or ]]> tag not allowed in any instance!");
				return null;
			}
		}
		
        String entireBuffer = fileBuffer.toString();
        int bufferLength = entireBuffer.length();
        int numOpenTags = 0;
        boolean insideTag = false;
        boolean insidecData = false;
        StringBuffer tagData = new StringBuffer();
        StringBuffer cData = new StringBuffer();
        Vector<String> tags = new Vector<String>();
        
        output.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<u:documentbody>\n");
        
        for(int i = 0; i < bufferLength; i++) {
            char currentChar = entireBuffer.charAt(i);

            if (insidecData || !insideTag) {
                if (currentChar != '<') {
                    cData.append(currentChar);
                }

                if (insidecData && currentChar == '<') {
                    cData.append("<");
                }

                if (cData.toString().endsWith("]]")) {
                    insidecData = false;
                }
            }

            if (currentChar == '<') {
                if (cData.length() > 0) {
                    output.append(cData.toString());
                    
                    cData.delete(0, cData.length());
                }

                numOpenTags++;

                if (numOpenTags > 1) {
                    if (tagData.toString().startsWith("![CDATA[")) {
                        insidecData = true;

                        cData.append("<");
                    }

                    tags.add(tagData.toString());
                    tagData.delete(0, tagData.length());
                }

                insideTag = true;
                continue;
            }

            if (currentChar == '>') {
                numOpenTags--;

                if (tagData.toString() != "") {
                    if (tagData.toString().endsWith("]]")) {
                        insidecData = false;
                    }

                    tags.add(tagData.toString() + ">");
                    tagData.delete(0, tagData.length());
                }

                if (numOpenTags == 0) {
                    insideTag = false;

                    for(int j = 0; j < tags.size(); j++) {
                        String tag = tags.get(j);
                        String tagName = null;
                        boolean knownTag = false;

                        if (tag.indexOf(" ") != -1) {
                            tagName = tag.substring(0, tag.indexOf(" "));
                        } else {
                            tagName = tag;
                        }

                        if (tagName.endsWith(">")) {
                            tagName = tagName.substring(0, tagName.length() - 1);
                        }

                        if (tagName.startsWith("/")) {
                            tagName = tagName.substring(1);
                        } else if (tagName.endsWith("/")) {
                            tagName = tagName.substring(0, tagName.length() - 1);
                        }

                        if (tagCache.get(tagName) != null || subtagCache.get(tagName) != null) {
                            knownTag = true;
                        }

                        if (knownTag) {
                            knownTags++;
                        } else {
                            unknownTags++;
                        }

                        if (tagName == null || tagName.equals("")) {
                            continue;
                        }

                        if (knownTag) {
                            output.append("<" + tag);
                        } else {
                            if (tag.startsWith("\"")) {
                                output.append("<![CDATA[" + tag + "]]>");
                            } else {
                                output.append("<![CDATA[<" + tag + "]]>");
                            }
                        }
                    }

                    tags.clear();
                }
                continue;
            }

            if (insideTag && !insidecData) {
                tagData.append(currentChar);
            }
        }

        output.append("</u:documentbody>\n");
        return output.toString();
	}
}